import {
  Component,
  OnInit,
  ViewEncapsulation,
  ChangeDetectionStrategy,
  Input,
  Output,
  EventEmitter,
  ChangeDetectorRef,
  OnChanges,
  SimpleChanges,
  AfterViewInit,
  OnDestroy,
  NgZone,
  ElementRef,
  Renderer2,
  TemplateRef,
  TrackByFunction,
} from '@angular/core';
import { Subject, merge, Subscription } from 'rxjs';
import { takeUntil, debounceTime, filter } from 'rxjs/operators';
import { isNil as _isNil, assign as _assign, some as _some } from 'lodash';
import { doFnResult } from '../core/utils/api';
import { LvConfig } from '../core/config';
import { TreeService } from './tree.service';
import { getDescendants } from './utils';
import { TreeNode, TreeCallback, EventName } from './interface';

@Component({
  selector: 'lv-tree-node',
  templateUrl: './tree-node.component.html',
  encapsulation: ViewEncapsulation.None,
  changeDetection: ChangeDetectionStrategy.OnPush,
  host: {
    class: 'lv-treenode',
    '[class.disabled]': 'treeNode.disabled',
    '[class.lv-treenode-expanded]': 'treeNode.expanded && !treeNode.isLeaf',
    '[class.lv-treenode-line]': 'isLine',
    '[class.lv-treenode-selected]': 'isSelected(treeNode)',
    '[class.lv-treenode-checkbox]': 'isCheckbox',
    // '(click)': '_click($event)',
    // '(dblclick)': '_dblClick($event)'
  },
})
export class TreeNodeComponent implements OnInit, OnChanges, AfterViewInit, OnDestroy {
  // 输入输出属性
  @Input() lvData: TreeNode; // 源数据
  @Input() lvBeforeDrop: TreeCallback; // NEED_DO
  @Input() lvNodeIsStart: boolean[] = [];
  @Input() lvIsNodeIsEnd: boolean[] = [];
  @Input() lvBeforeSelected: TreeCallback; // 选中前执行的方法

  @Input() lvBeforeExpanded: TreeCallback; // NEED_DO: 展开前执行的方法，返回false
  @Input() lvHighlightContent: string = '';
  @Output() lvExpandedChange = new EventEmitter<any>(); // 节点展开变化回调

  @Input() lvShowExpandedIcon: boolean | { expandIcon: string; collapseIcon: string } = true; // 展开折叠图标

  isLine: boolean;
  lvVirtualItemSize; // 每行高度
  treeNode: TreeNode;
  lvVirtualMaxBuffer; // 渲染新项时最大缓冲高度
  lvVirtualMinBuffer; // 视图之外渲染的最小缓冲区数，若缓存区数量低于此数量将渲染新项
  isCheckbox: boolean;

  lvVirtualScroll: boolean; // 虚拟滚动

  descendantsNodes: TreeNode[]; // 所有子代

  nodeTemplate: TemplateRef<any>;

  lvTrackBy: TrackByFunction<any>; // 内容列表trackBy

  // 外部可访问变量
  refreshView$ = new Subject<void>();

  public assetsPrefixPath = LvConfig.assetsPrefixPath;
  private _rootComponent;
  private _lineSub = Subscription.EMPTY;
  private _destroy$ = new Subject<void>();
  private _expanded$ = new Subject<void>();
  private _viewinit$ = new Subject<TreeNode>();

  // 内部变量
  private _defaultExpandedIcon = {
    true: 'lv-icon-tree-unfold',
    false: 'lv-icon-tree-fold',
  };

  private _defaultContentIcon = {
    // NEED_DO: 替换为实际3.0默认图标
    true: 'lv-icon-data-center',
    false: 'lv-icon-data-center',
  };

  private _defaultLeafContentIcon = {
    // NEED_DO: 替换为实际3.0默认图标
    true: 'lv-icon-data-center',
    false: 'lv-icon-data-center',
  };

  constructor(
    private treeService: TreeService,
    private _cdr: ChangeDetectorRef,
    private _ngZone: NgZone,
    private _element: ElementRef,
    private _renderer: Renderer2,
  ) {
    this._rootComponent = this.treeService.getRootComponent();
  }

  get blockNode(): boolean {
    return this._rootComponent?.lvBlockNode;
  }

  get isAllCheck(): any {
    const selected = this.isSelected(this.treeNode);
    return selected;
  }

  get isHalfCheck(): any {
    const isHalfChecked = this.isHalfSelected(this.treeNode);
    return isHalfChecked;
  }

  get isContentToggleIcon(): boolean {
    return this._rootComponent?.lvShowContentIcon && this.contentToggleIcon[!!this.treeNode.expanded as any] !== '';
  }

  get contentToggleIcon(): { [key: string]: any } {
    return this._getToggleIcon(
      this.treeNode.contentToggleIcon,
      this.treeNode.isLeaf ? this._defaultLeafContentIcon : this._defaultContentIcon,
    );
  }

  get isExpandedToggleIcon(): boolean {
    const showIcon =
      this.lvShowExpandedIcon instanceof Object
        ? this.lvShowExpandedIcon.expandIcon || this.lvShowExpandedIcon.collapseIcon
        : this.lvShowExpandedIcon;
    return showIcon && !this.treeNode.isLeaf;
  }

  get expandedToggleIcon(): { [key: string]: any } {
    const convertExpandedIcon = (): { true?: string; false?: string } | string | { true: any; false: any } => {
      const expandedIcon =
        this.lvShowExpandedIcon instanceof Object
          ? { true: this.lvShowExpandedIcon.expandIcon, false: this.lvShowExpandedIcon.collapseIcon }
          : this._defaultExpandedIcon;
      return this.treeNode.expandedToggleIcon || expandedIcon;
    };
    return this._getToggleIcon(convertExpandedIcon(), this._defaultExpandedIcon);
  }

  expandedChange(e): void {
    this.lvExpandedChange.emit(e);
  }

  _click(e: MouseEvent): void {
    this._emitEvent(e, EventName.CLICK);
  }

  ngAfterViewInit(): void {
    this._viewinit$.next(this.treeNode); // 初始化广播
  }

  _dblClick(e: MouseEvent): void {
    this._emitEvent(e, EventName.DBLCLICK);
  }

  ngOnDestroy(): void {
    this._destroy$.next();
    this._destroy$.complete();
  }

  isSelected(source: TreeNode): any {
    return this._rootComponent.isSelected(source);
  }

  getNodeBySelector(source, selector: string): any {
    return source.querySelector(selector);
  }

  isHalfSelected(source: TreeNode): any {
    return this._rootComponent.isHalfSelected(source);
  }

  // 往子孙找能找到的选中项
  _getSelectedChildren(): any {
    return _some(this.descendantsNodes, (item): any => this._rootComponent.isSelected(item));
  }

  dbClickNode(e: MouseEvent, source: TreeNode): void {
    if (source.disabled) {
      return;
    }
    this._emitEvent(e, EventName.DBLCLICK);
  }

  clickNode(e: MouseEvent, source: TreeNode): void {
    if (source.disabled) {
      return;
    }

    this.selectedNodeContent(e);

    this._emitEvent(e, EventName.CLICK, false);
  }

  ngOnInit(): void {
    this._subscribeRefreshView();
    this._rootComponent.selectionChange$.subscribe((): void => {
      this.descendantsNodes = getDescendants(this.treeNode);
    });
  }

  selectedNodeContent(e: MouseEvent): void {
    const onlyCheckbox = this._rootComponent.lvSelectedByCheckbox;
    if (!this._rootComponent.lvSelectionMode || onlyCheckbox) {
      return;
    } else {
      this.selectedNode(e, this.treeNode);
    }
  }

  /**
   * 获取toggleIcon
   * source === undefined | null 默认原始icon
   * source === string 默认toggle为同一icon
   * source === obj 覆盖原始
   */
  _getToggleIcon(source: any, origin: any): any {
    let output = source;
    if (_isNil(source)) {
      return origin;
    }
    if (typeof source === 'string') {
      output = { true: source, false: source };
    }
    return _assign(origin, output);
  }

  expanded(e: MouseEvent): void {
    e.stopPropagation();
    const node = this.treeNode;
    node.expanded = !node.expanded;
    this.refreshView$.next();
    this.treeService.setExpandedStream(this.treeNode); // 展开节点
    this.expandedChange(this.treeNode);
  }

  _emitEvent(e: MouseEvent, type: string, stopPropagation: boolean = true): void {
    e.preventDefault();
    if (stopPropagation) {
      e.stopPropagation();
    }
    this._rootComponent.emitEvent({ type, node: this.treeNode, event: e });
    this.treeService.emitEvent();
  }

  ngOnChanges(changes: SimpleChanges): void {
    const { lvData: dataChange } = changes;
    if (dataChange) {
      this.treeNode = dataChange.currentValue || {};

      if (this.treeNode.parent) {
        this.treeNode.level = this.treeNode.parent.level + 1;
      } else {
        this.treeNode.level = 0;
      }
    }
    this._initData();
  }

  // 订阅更新视图的流
  _subscribeRefreshView(): void {
    merge(
      this.refreshView$,
      this.treeService.subscribeEvent(),
      this._rootComponent.selectionChange$,
      this._rootComponent.lvSelectionChange,
      this._rootComponent.refreshView$,
    )
      .pipe(debounceTime(1), takeUntil(this._destroy$))
      .subscribe((): void => this._cdr.markForCheck());
  }

  selectedNode(e: MouseEvent, source: TreeNode): void {
    e.stopPropagation();
    if (source.disabled) {
      return;
    }

    const fn = (event): void => {
      // 切换选中项并触发change
      this._rootComponent.toggleSelection(source);
      this._emitEvent(event, EventName.CHECK);
    };

    if (!this.lvBeforeSelected) {
      fn(e);
    } else {
      const call = this.lvBeforeSelected(this.treeNode);
      doFnResult(call, (result): void => {
        if (result) {
          fn(e);
        } else {
          // 自定义点击文本执行展开功能，对象属性变更change无法检测，手动执行一次expand node变更
          this.treeService.setExpandedStream(this.treeNode); // 展开节点
          this.expandedChange(this.treeNode);
          return;
        }
      });
    }
  }

  // 初始化初始值
  _initData(): void {
    if (this._rootComponent) {
      const getCheckbox = (): boolean => {
        if (this._rootComponent.lvShowCheckbox && this._rootComponent.lvSelectionMode === 'multiple') {
          return this.treeNode?.showCheckbox !== undefined && !this.treeNode?.showCheckbox ? false : true;
        } else {
          return false;
        }
      };
      this.isCheckbox = getCheckbox();
      this.nodeTemplate = this._rootComponent.lvNodeTemplate;
      this.isLine = this._rootComponent.lvShowLine;
      this.lvVirtualScroll = this._rootComponent.lvVirtualScroll; // 虚拟滚动
      this.lvVirtualItemSize = this._rootComponent.lvVirtualItemSize; // 每行高度
      this.lvVirtualMaxBuffer = this._rootComponent.lvVirtualMaxBuffer; // 渲染新项时最大缓冲高度
      this.lvVirtualMinBuffer = this._rootComponent.lvVirtualMinBuffer; // 视图之外渲染的最小缓冲区数，若缓存区数量低于此数量将渲染新项
      this.lvTrackBy = this._rootComponent.lvTrackBy; // 内容列表trackBy
    }

    this.descendantsNodes = getDescendants(this.treeNode);
  }

  /**
   * 刷新连接线高度：当最后一个子节点被展开时会显示异常
   */
  _refreshLine(): void {
    if (!this.isLine) {
      return;
    }
    const node = this.treeNode;
    const mainSelector = '.lv-treenode-main';
    const childrenSelector = '.lv-treenode-children';
    const lineSelectior = '.lv-treenode-parent-line';
    const rootDom = this._element.nativeElement;
    const sourceDom = this.getNodeBySelector(rootDom, mainSelector);

    const filterStream = (): boolean => {
      const { children } = node;
      if (!node.expanded || !children || !children.length) {
        return false;
      }
      const last = children.slice(-1)[0];
      if (last.isLeaf) {
        const lineDom = this.getNodeBySelector(rootDom, lineSelectior);
        if (lineDom) {
          lineDom.style.height = '';
          lineDom.style.bottom = '';
        }
        return false;
      }
      return true;
    };

    this._ngZone.runOutsideAngular((): void => {
      this._lineSub.unsubscribe();

      this._lineSub = merge(this.treeService.getExpandedStream(), this.treeService.getLineChangeStream())
        .pipe(debounceTime(10), filter(filterStream), takeUntil(this._destroy$))
        .subscribe((res): void => {
          const destDom = this.getNodeBySelector(rootDom, childrenSelector).lastElementChild;
          const lineDom = this.getNodeBySelector(rootDom, lineSelectior);
          const height = destDom.getBoundingClientRect().top - sourceDom.getBoundingClientRect().top - 16;
          lineDom.style.height = `${height}px`;
          lineDom.style.bottom = 'auto';
        });
    });
  }
}
